//#define __COMPILE_DEBUG__
#include <wchar.h>
#include <string.h>
#include "../zepto.h"
#include "../internal.h"

DECLARE_FOREIGN("BOOLEAN=?", builtin_boolean_eq, ONE_ARG, EVAL_FIRST, HAVE_TAIL, EVAL_TAIL)
{
  CHECK_TYPE(arg1, ZEPTO_BOOL);
  CHECK_TYPE(arg2, ZEPTO_BOOL);

  if(arg1 != arg2) {
    return FALSE;
  }
  
  while(tail->type == ZEPTO_PAIR) {
    CHECK_TYPE(CAR(tail), ZEPTO_BOOL);
    if(CAR(tail) != arg1) {
      return FALSE;
    }
    tail = CDR(tail);
  }

  return TRUE;
}

static inline int do_strcmp(struct string *x, struct string *y)
{
  if(x->length != y->length) {
    return 1;
  }

  return memcmp(x->data, y->data, x->length * sizeof(x->data[0]));
}

// TODO: remove later
/*
DECLARE_FOREIGN("STRING=?", builtin_string_eq, ONE_ARG, EVAL_FIRST, HAVE_TAIL, EVAL_TAIL)
{
  CHECK_TYPE(arg1, ZEPTO_STRING);

  while(tail->type == ZEPTO_PAIR) {
    CHECK_TYPE(CAR(tail), ZEPTO_STRING);
    if(wcscmp(CAR(tail)->data.string.data, arg1->data.string.data)) {
      return FALSE;
    }
    tail = CDR(tail);
  }

  return TRUE;
}
*/

#define MK_NUM_PRED(str, name, pred)                                    \
  DECLARE_FOREIGN(str, name, ONE_ARG,                                   \
                  EVAL_FIRST, HAVE_TAIL, EVAL_TAIL)                     \
  {                                                                     \
    CHECK_TYPE(arg1, ZEPTO_INTEGER);                                    \
                                                                        \
    while(tail->type == ZEPTO_PAIR) {                                   \
      CHECK_TYPE(CAR(tail), ZEPTO_INTEGER);                             \
      if(!(arg1->data.integer pred CAR(tail)->data.integer)) {          \
        DEBUG("FALSE: %d %d", CAR(tail)->data.integer,                  \
              arg1->data.integer);                                      \
        return FALSE;                                                   \
      }                                                                 \
      tail = CDR(tail);                                                 \
    }                                                                   \
                                                                        \
    return TRUE;                                                        \
  }

MK_NUM_PRED("=", builtin_number_eq, ==);
MK_NUM_PRED("<", builtin_number_l, <);
MK_NUM_PRED("<=", builtin_number_le, <=);
MK_NUM_PRED(">", builtin_number_g, >);
MK_NUM_PRED(">=", builtin_number_ge, >=);

DECLARE_FOREIGN("SYMBOL=?", builtin_symbol_eq, ONE_ARG, EVAL_FIRST, HAVE_TAIL, EVAL_TAIL)
{
  CHECK_TYPE(arg1, ZEPTO_SYMBOL);

  while(tail->type == ZEPTO_PAIR) {
    CHECK_TYPE(CAR(tail), ZEPTO_SYMBOL);
    if(CAR(tail) != arg1) {
      return FALSE;
    }
    tail = CDR(tail);
  }

  return TRUE;
}

DECLARE_FOREIGN("EQ?", builtin_eq, ONE_ARG, EVAL_FIRST, HAVE_TAIL, EVAL_TAIL)
{
  enum zepto_type type;
  struct zepto *tmp;

  type = arg1->type;

  tmp = tail;

  while(tmp->type == ZEPTO_PAIR) {
    if(CAR(tmp)->type != type) {
      return FALSE;
    }
    tmp = CDR(tmp);
  }

  switch(type) {
  case ZEPTO_SYMBOL:
    return builtin_symbol_eq(env, arg1, arg2, tail);
  case ZEPTO_BOOL:
    return builtin_boolean_eq(env, arg1, arg2, tail);
#if 0
    //TODO: eq should use string comparison as set instandard
  case ZEPTO_STRING:
    return builtin_string_eq(env, arg1, arg2, tail);
#endif
  case ZEPTO_INTEGER:
    return builtin_number_eq(env, arg1, arg2, tail);
  default:
    break;
  }
  
  while(tail->type == ZEPTO_PAIR) {
    if(CAR(tail) != arg1) {
      return FALSE;
    }
    tail = CDR(tail);
  }
  
  return TRUE;
}

void register_equality(void)
{
  EXPORT_FOREIGN(builtin_boolean_eq);
  EXPORT_FOREIGN(builtin_number_eq);
  //  EXPORT_FOREIGN(builtin_string_eq);
  EXPORT_FOREIGN(builtin_symbol_eq);
  EXPORT_FOREIGN(builtin_eq);
  EXPORT_FOREIGN(builtin_number_l);
  EXPORT_FOREIGN(builtin_number_le);
  EXPORT_FOREIGN(builtin_number_g);
  EXPORT_FOREIGN(builtin_number_ge);
}

